index.js ➔ expand   F
last analyzed

Complexity

Conditions 25
Paths 40

Size

Total Lines 101
Code Lines 69

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 25
eloc 69
nc 40
nop 2
dl 0
loc 101
rs 0
c 0
b 0
f 0

2 Functions

Rating   Name   Duplication   Size   Complexity  
A index.js ➔ ... ➔ concatMap 0 1 1
A index.js ➔ ... ➔ post.map 0 3 1

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like index.js ➔ expand often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
var concatMap = require('concat-map');
2
var balanced = require('balanced-match');
3
4
module.exports = expandTop;
5
6
var escSlash = '\0SLASH'+Math.random()+'\0';
7
var escOpen = '\0OPEN'+Math.random()+'\0';
8
var escClose = '\0CLOSE'+Math.random()+'\0';
9
var escComma = '\0COMMA'+Math.random()+'\0';
10
var escPeriod = '\0PERIOD'+Math.random()+'\0';
11
12
function numeric(str) {
13
  return parseInt(str, 10) == str
14
    ? parseInt(str, 10)
15
    : str.charCodeAt(0);
16
}
17
18
function escapeBraces(str) {
19
  return str.split('\\\\').join(escSlash)
20
            .split('\\{').join(escOpen)
21
            .split('\\}').join(escClose)
22
            .split('\\,').join(escComma)
23
            .split('\\.').join(escPeriod);
24
}
25
26
function unescapeBraces(str) {
27
  return str.split(escSlash).join('\\')
28
            .split(escOpen).join('{')
29
            .split(escClose).join('}')
30
            .split(escComma).join(',')
31
            .split(escPeriod).join('.');
32
}
33
34
35
// Basically just str.split(","), but handling cases
36
// where we have nested braced sections, which should be
37
// treated as individual members, like {a,{b,c},d}
38
function parseCommaParts(str) {
39
  if (!str)
40
    return [''];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
41
42
  var parts = [];
43
  var m = balanced('{', '}', str);
44
45
  if (!m)
46
    return str.split(',');
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
47
48
  var pre = m.pre;
49
  var body = m.body;
50
  var post = m.post;
51
  var p = pre.split(',');
52
53
  p[p.length-1] += '{' + body + '}';
54
  var postParts = parseCommaParts(post);
55
  if (post.length) {
56
    p[p.length-1] += postParts.shift();
57
    p.push.apply(p, postParts);
58
  }
59
60
  parts.push.apply(parts, p);
61
62
  return parts;
63
}
64
65
function expandTop(str) {
66
  if (!str)
67
    return [];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
68
69
  // I don't know why Bash 4.3 does this, but it does.
70
  // Anything starting with {} will have the first two bytes preserved
71
  // but *only* at the top level, so {},a}b will not expand to anything,
72
  // but a{},b}c will be expanded to [a}c,abc].
73
  // One could argue that this is a bug in Bash, but since the goal of
74
  // this module is to match Bash's rules, we escape a leading {}
75
  if (str.substr(0, 2) === '{}') {
76
    str = '\\{\\}' + str.substr(2);
77
  }
78
79
  return expand(escapeBraces(str), true).map(unescapeBraces);
80
}
81
82
function identity(e) {
83
  return e;
84
}
85
86
function embrace(str) {
87
  return '{' + str + '}';
88
}
89
function isPadded(el) {
90
  return /^-?0\d/.test(el);
91
}
92
93
function lte(i, y) {
94
  return i <= y;
95
}
96
function gte(i, y) {
97
  return i >= y;
98
}
99
100
function expand(str, isTop) {
101
  var expansions = [];
102
103
  var m = balanced('{', '}', str);
104
  if (!m || /\$$/.test(m.pre)) return [str];
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
105
106
  var isNumericSequence = /^-?\d+\.\.-?\d+(?:\.\.-?\d+)?$/.test(m.body);
107
  var isAlphaSequence = /^[a-zA-Z]\.\.[a-zA-Z](?:\.\.-?\d+)?$/.test(m.body);
108
  var isSequence = isNumericSequence || isAlphaSequence;
109
  var isOptions = m.body.indexOf(',') >= 0;
110
  if (!isSequence && !isOptions) {
111
    // {a},b}
112
    if (m.post.match(/,.*\}/)) {
113
      str = m.pre + '{' + m.body + escClose + m.post;
114
      return expand(str);
115
    }
116
    return [str];
117
  }
118
119
  var n;
120
  if (isSequence) {
121
    n = m.body.split(/\.\./);
122
  } else {
123
    n = parseCommaParts(m.body);
124
    if (n.length === 1) {
125
      // x{{a,b}}y ==> x{a}y x{b}y
126
      n = expand(n[0], false).map(embrace);
127
      if (n.length === 1) {
128
        var post = m.post.length
129
          ? expand(m.post, false)
130
          : [''];
131
        return post.map(function(p) {
132
          return m.pre + n[0] + p;
133
        });
134
      }
135
    }
136
  }
137
138
  // at this point, n is the parts, and we know it's not a comma set
139
  // with a single entry.
140
141
  // no need to expand pre, since it is guaranteed to be free of brace-sets
142
  var pre = m.pre;
143
  var post = m.post.length
0 ignored issues
show
Comprehensibility Naming Best Practice introduced by
The variable post already seems to be declared on line 128. Consider using another variable name or omitting the var keyword.

This check looks for variables that are declared in multiple lines. There may be several reasons for this.

In the simplest case the variable name was reused by mistake. This may lead to very hard to locate bugs.

If you want to reuse a variable for another purpose, consider declaring it at or near the top of your function and just assigning to it subsequently so it is always declared.

Loading history...
144
    ? expand(m.post, false)
145
    : [''];
146
147
  var N;
148
149
  if (isSequence) {
150
    var x = numeric(n[0]);
151
    var y = numeric(n[1]);
152
    var width = Math.max(n[0].length, n[1].length)
153
    var incr = n.length == 3
154
      ? Math.abs(numeric(n[2]))
155
      : 1;
156
    var test = lte;
157
    var reverse = y < x;
158
    if (reverse) {
159
      incr *= -1;
160
      test = gte;
161
    }
162
    var pad = n.some(isPadded);
163
164
    N = [];
165
166
    for (var i = x; test(i, y); i += incr) {
167
      var c;
168
      if (isAlphaSequence) {
169
        c = String.fromCharCode(i);
170
        if (c === '\\')
171
          c = '';
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
172
      } else {
173
        c = String(i);
174
        if (pad) {
175
          var need = width - c.length;
176
          if (need > 0) {
177
            var z = new Array(need + 1).join('0');
178
            if (i < 0)
179
              c = '-' + z + c.slice(1);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
180
            else
181
              c = z + c;
182
          }
183
        }
184
      }
185
      N.push(c);
186
    }
187
  } else {
188
    N = concatMap(n, function(el) { return expand(el, false) });
189
  }
190
191
  for (var j = 0; j < N.length; j++) {
192
    for (var k = 0; k < post.length; k++) {
193
      var expansion = pre + N[j] + post[k];
194
      if (!isTop || isSequence || expansion)
195
        expansions.push(expansion);
0 ignored issues
show
Coding Style Best Practice introduced by
Curly braces around statements make for more readable code and help prevent bugs when you add further statements.

Consider adding curly braces around all statements when they are executed conditionally. This is optional if there is only one statement, but leaving them out can lead to unexpected behaviour if another statement is added later.

Consider:

if (a > 0)
    b = 42;

If you or someone else later decides to put another statement in, only the first statement will be executed.

if (a > 0)
    console.log("a > 0");
    b = 42;

In this case the statement b = 42 will always be executed, while the logging statement will be executed conditionally.

if (a > 0) {
    console.log("a > 0");
    b = 42;
}

ensures that the proper code will be executed conditionally no matter how many statements are added or removed.

Loading history...
196
    }
197
  }
198
199
  return expansions;
200
}
201
202